﻿
Imports System.IO
Imports std
Imports storoomc

'low performance in all serialstore functions
Namespace serialstore
    Public Module serialstore

        Public Const lengthkeySuffix As Char = character.dollar
        Public fragmentSize As UInt64 = 1 * 1024 * 1024     '1M

        Private Function lengthkey(ByVal key As String) As String
            Return generatekey(key, lengthkeySuffix)
        End Function

        'not a safe call, but somehow, maybe
        Private Function islengthkey(ByVal key As String) As Boolean
            Return strendwith(key, suffixSeparator + lengthkeySuffix)
        End Function

        Private Delegate Function containsKeyCallback(ByVal key As String) As Boolean

        Private Function islengthkey(ByVal key As String, ByVal sizeofKey As UInt64, _
                                     ByVal containsKeyCB As containsKeyCallback) As Boolean
            assert(Not containsKeyCB Is Nothing, "containsKeyCB is nothing.")
            Dim basekey As String = Nothing
            If breaklengthkey(key, basekey) Then
                If sizeofKey > 0 Then
                    Dim i As Int64
                    For i = 0 To sizeofKey - 1
                        If Not containsKeyCB(generatekey(basekey, i)) Then
                            Exit For
                        End If
                    Next

                    Return i = sizeofKey
                Else
                    Return True
                End If
            Else
                Return False
            End If
        End Function

        Private Function breaklengthkey(ByVal full As String, ByRef key As String) As Boolean
            If islengthkey(full) Then
                key = strleft(full, strlen(full) - strlen(suffixSeparator + lengthkeySuffix))
                Return True
            Else
                Return False
            End If
        End Function

        Private Sub initialtalk(ByRef talk As storoomTalk, ByVal host As String, ByVal port As Int32, _
                                ByVal holdtime As Int64, ByVal livetime As Int64)
            If talk Is Nothing Then
                talk = storoom
            End If
            talk.initial(host, port, talkTime.client2room.retrytimes)
            If holdtime > npos Then
                talk.holdTime = holdtime
            End If
            If livetime > npos Then
                talk.liveTime = livetime
            End If
        End Sub

        Public Function modify(ByVal host As String, ByVal port As Int32, ByVal key As String, _
                               ByVal s As Stream, Optional ByVal talk As storoomTalk = Nothing, _
                               Optional ByVal holdtime As Int64 = npos, _
                               Optional ByVal livetime As Int64 = npos) As Boolean
            If key Is Nothing OrElse s Is Nothing Then
                Return False
            End If

            initialtalk(talk, host, port, holdtime, livetime)
            Return modify(key, s, talk)
        End Function

        Public Function modify(ByVal key As String, ByVal s As Stream, _
                               Optional ByVal talk As storoomTalk = Nothing) As Boolean
            If key Is Nothing OrElse s Is Nothing Then
                Return False
            End If

            Return delete(key, talk) AndAlso write(key, s, talk)
        End Function

        Public Function write(ByVal host As String, ByVal port As Int32, ByVal key As String, ByVal s As Stream, _
                              Optional ByVal talk As storoomTalk = Nothing, _
                              Optional ByVal holdtime As Int64 = npos, _
                              Optional ByVal livetime As Int64 = npos) As Boolean
            If key Is Nothing OrElse s Is Nothing Then
                Return False
            End If

            initialtalk(talk, host, port, holdtime, livetime)
            Return write(key, s, talk)
        End Function

        Private Function writeImpl(ByVal key As String, ByVal s As Stream, _
                                   Optional ByVal talk As storoomTalk = Nothing) As Boolean
            If key Is Nothing OrElse s Is Nothing Then
                'though key will be checked later, but save sometime now
                Return False
            End If

            If talk Is Nothing Then
                talk = storoom
            End If

            assert(fragmentSize > 0, "fragmentSize should over 0.")
            Dim readsize As Int64
            readsize = fragmentSize
            Dim buff As bytes = Nothing
            Static sizeplus As bytes = New bytes(Convert.ToByte("H"c))
            Dim index As Int64
            Dim rtn As Boolean
            rtn = True
            If Not talk.sizeof(lengthkey(key), index) Then
                raiseError("cannot read existing sizeof lengthkey, key " + key + _
                           ", lengthkey " + lengthkey(key) + ", assume 0", _
                           errorHandle.errorType.warning)
                index = 0
            End If
            While readsize = fragmentSize
                readsize = readstream(s, buff, length:=fragmentSize)
                If readsize > 0 Then
                    If buff Is Nothing OrElse buff.size() <> readsize Then
                        debugassert(False, "buff.size should be the same as readsize.")
                        rtn = False
                    ElseIf Not talk.write(lengthkey(key), sizeplus) Then
                        raiseError("cannot add length to key " + key + ", lengthkey " + lengthkey(key), _
                                   errorHandle.errorType.exclamation)
                        rtn = False
                    ElseIf Not talk.uniqueWrite(generatekey(key, index), buff) Then
                        raiseError("cannot write data to key " + key + _
                                   ", destination key " + generatekey(key, index), _
                                   errorHandle.errorType.exclamation)
                        rtn = False
                    End If
                End If
                index += 1
            End While

            Return rtn
        End Function

        Public Function write(ByVal key As String, ByVal s As Stream, _
                              Optional ByVal talk As storoomTalk = Nothing) As Boolean
            If locks.lock(key, talk) Then
                Dim rtn As Boolean
                rtn = writeImpl(key, s, talk)
                locks.release(key, talk)
                Return rtn
            Else
                Return False
            End If
        End Function

        Public Function delete(ByVal host As String, ByVal port As Int32, ByVal key As String, _
                               Optional ByVal talk As storoomTalk = Nothing, _
                               Optional ByVal holdtime As Int64 = npos, _
                               Optional ByVal livetime As Int64 = npos) As Boolean
            If key Is Nothing Then
                Return False
            End If

            initialtalk(talk, host, port, holdtime, livetime)
            Return delete(key, talk)
        End Function

        Public Function delete(ByVal key As String, Optional ByVal talk As storoomTalk = Nothing) As Boolean
            If key Is Nothing Then
                Return False
            End If

            If talk Is Nothing Then
                talk = storoom
            End If

            If locks.lock(key, talk) Then
                Dim rtn As Boolean
                Dim length As UInt64
                rtn = True
                If Not talk.sizeof(lengthkey(key), length) Then
                    raiseError("cannot read length of key " + key + ", lengthkey " + lengthkey(key), _
                               errorHandle.errorType.exclamation)
                    rtn = False
                ElseIf Not talk.delete(lengthkey(key)) Then
                    raiseError("cannot delete length key of " + key + ", lengthkey " + lengthkey(key), _
                               errorHandle.errorType.exclamation)
                    rtn = False
                ElseIf length > 0 Then
                    Dim i As Int64
                    For i = 0 To length - 1
                        If Not talk.delete(generatekey(key, i)) Then
                            raiseError("cannot delete data of key " + key + ", destination key " + generatekey(key, i), _
                                       errorHandle.errorType.exclamation)
                            rtn = False
                        End If
                    Next
                End If

                locks.release(key, talk)
                Return rtn
            Else
                Return False
            End If
        End Function

        Public Delegate Sub readCallback(ByVal buff As bytes)
        Public Delegate Sub readCallback2(ByVal buff As bytes, ByVal o As Object)

        Private Sub defaultReadCB2(ByVal buff As bytes, ByVal o As Object)
            Dim cb As readCallback = Nothing
            cb = convertor.cast(Of readCallback)(o)
            cb(buff)
        End Sub

        Public Function read(ByVal host As String, ByVal port As Int32, ByVal key As String, ByVal cb As readCallback, _
                             Optional ByVal talk As storoomTalk = Nothing, _
                             Optional ByVal holdtime As Int64 = npos, _
                             Optional ByVal livetime As Int64 = npos) As Boolean
            If key Is Nothing OrElse cb Is Nothing Then
                Return False
            End If

            initialtalk(talk, host, port, holdtime, livetime)
            Return read(key, cb, talk)
        End Function

        Public Function read(ByVal host As String, ByVal port As Int32, ByVal key As String, _
                             ByVal cb As readCallback2, ByVal callbackObject As Object, _
                             Optional ByVal talk As storoomTalk = Nothing, _
                             Optional ByVal holdtime As Int64 = npos, _
                             Optional ByVal livetime As Int64 = npos) As Boolean
            If key Is Nothing OrElse cb Is Nothing Then
                Return False
            End If

            initialtalk(talk, host, port, holdtime, livetime)
            Return read(key, cb, callbackObject, talk)
        End Function

        Public Function read(ByVal key As String, ByVal cb As readCallback, _
                             Optional ByVal talk As storoomTalk = Nothing) As Boolean
            If key Is Nothing OrElse cb Is Nothing Then
                Return False
            End If

            Return read(key, AddressOf defaultReadCB2, cb, talk)
        End Function

        Public Function read(ByVal key As String, ByVal cb As readCallback2, ByVal callbackObject As Object, _
                             Optional ByVal talk As storoomTalk = Nothing) As Boolean
            If key Is Nothing OrElse cb Is Nothing Then
                Return False
            End If

            If talk Is Nothing Then
                talk = storoom
            End If

            If locks.lock(key, talk) Then
                Dim rtn As Boolean
                Dim length As UInt64
                rtn = True
                If Not talk.sizeof(lengthkey(key), length) Then
                    raiseError("cannot read length of key " + key + ", lengthkey " + lengthkey(key), _
                               errorHandle.errorType.exclamation)
                    rtn = False
                ElseIf length > 0 Then
                    Dim i As Int64
                    Dim buff As bytes = Nothing
                    For i = 0 To length - 1
                        If Not talk.read(generatekey(key, i), buff) Then
                            raiseError("cannot read data of key " + key + ", destination key " + generatekey(key, i), _
                                       errorHandle.errorType.exclamation)
                            rtn = False
                        Else
                            cb(buff, callbackObject)
                        End If
                    Next
                End If

                locks.release(key, talk)
                Return rtn
            Else
                Return False
            End If
        End Function

        Public Function list(ByVal host As String, ByVal port As Int32, ByRef l As vector(Of String), _
                             Optional ByVal talk As storoomTalk = Nothing, _
                             Optional ByVal holdtime As Int64 = npos, _
                             Optional ByVal livetime As Int64 = npos, _
                             Optional ByVal safer As Boolean = True) As Boolean
            initialtalk(talk, host, port, holdtime, livetime)
            Return list(l, talk, safer)
        End Function

        'not a good solution, but avoid to break storoom too much
        Public Function list(ByRef l As vector(Of String), Optional ByVal talk As storoomTalk = Nothing, _
                             Optional ByVal safer As Boolean = True) As Boolean
            If talk Is Nothing Then
                talk = storoom
            End If

            Dim s As [set](Of String) = Nothing
            If Not talk.list(s) Then
                raiseError("cannot list", errorHandle.errorType.exclamation)
                Return False
            ElseIf Not s Is Nothing Then
                If l Is Nothing Then
                    l = New vector(Of String)()
                Else
                    l.clear()
                End If

                Dim it As [set](Of String).iterator = Nothing
                it = s.begin()
                While it <> s.end()
                    Dim iskey As Boolean = False
                    If safer Then
                        If islengthkey(+it) Then
                            Dim sizeofKey As UInt64
                            If Not talk.sizeof(+it, sizeofKey) Then
                                raiseError("cannot get size of " + (+it) + ", ignore.", errorHandle.errorType.warning)
                                iskey = False
                            ElseIf islengthkey(+it, sizeofKey, (Function(key As String) s.find(key) <> s.end())) Then
                                iskey = True
                            End If
                        Else
                            iskey = False
                        End If
                    Else
                        iskey = islengthkey(+it)
                    End If
                    If iskey Then
                        Dim key As String = Nothing
                        If breaklengthkey(+it, key) Then
                            l.push_back(key)
                        End If
                    End If
                    it += 1
                End While

                Return True
            Else
                raiseError("talk.list returns nothing, but return value is true.", errorHandle.errorType.exclamation)
                Return False
            End If
        End Function

        Public Function sizeof(ByVal host As String, ByVal port As Int32, ByVal key As String, ByRef size As UInt64, _
                               Optional ByVal talk As storoomTalk = Nothing, _
                               Optional ByVal holdtime As Int64 = npos, _
                               Optional ByVal livetime As Int64 = npos) As Boolean
            initialtalk(talk, host, port, holdtime, livetime)
            Return sizeof(key, size, talk)
        End Function

        Public Function sizeof(ByVal key As String, ByRef size As UInt64, _
                               Optional ByVal talk As storoomTalk = Nothing) As Boolean
            If talk Is Nothing Then
                talk = storoom
            End If

            Dim sizeofkey As UInt64
            If Not talk.sizeof(lengthkey(key), sizeofkey) Then
                raiseError("cannot read sizeof key " + key + ", lengthkey " + lengthkey(key), _
                           errorHandle.errorType.exclamation)
                Return False
            Else
                Dim rtn As Boolean = True
                size = 0
                If sizeofkey > 0 Then
                    Dim i As Int64
                    For i = 0 To sizeofkey - 1
                        Dim this As UInt64
                        If Not talk.sizeof(generatekey(key, i), this) Then
                            raiseError("cannot read sizeof key " + key + ", destination key " + generatekey(key, i), _
                                       errorHandle.errorType.exclamation)
                            rtn = False
                        Else
                            size += this
                        End If
                    Next

                    Return rtn
                Else
                    Return True
                End If
            End If
        End Function

        Public Function seek(ByVal host As String, ByVal port As Int32, ByVal key As String, _
                             Optional ByVal talk As storoomTalk = Nothing, _
                             Optional ByVal holdtime As Int64 = npos, _
                             Optional ByVal livetime As Int64 = npos) As Boolean
            initialtalk(talk, host, port, holdtime, livetime)
            Return seek(key, talk)
        End Function

        Public Function seek(ByVal key As String, Optional ByVal talk As storoomTalk = Nothing) As Boolean
            If talk Is Nothing Then
                talk = storoom
            End If

            Return talk.seek(lengthkey(key))
        End Function

        Public Function uniqueWrite(ByVal host As String, ByVal port As Int32, _
                                    ByVal key As String, ByVal s As Stream, _
                                    Optional ByVal talk As storoomTalk = Nothing, _
                                    Optional ByVal holdtime As Int64 = npos, _
                                    Optional ByVal livetime As Int64 = npos) As Boolean
            initialtalk(talk, host, port, holdtime, livetime)
            Return uniqueWrite(key, s, talk)
        End Function

        Public Function uniqueWrite(ByVal key As String, ByVal s As Stream, _
                                    Optional ByVal talk As storoomTalk = Nothing) As Boolean
            If talk Is Nothing Then
                talk = storoom
            End If

            If locks.lock(key, talk) Then
                Dim rtn As Boolean
                rtn = Not seek(key, talk) AndAlso writeImpl(key, s, talk)
                locks.release(key, talk)

                Return rtn
            Else
                Return False
            End If
        End Function
    End Module
End Namespace
